package org.keyroy.app.puzzle_rpg.views;

import java.util.ArrayList;
import java.util.List;

import org.keyroy.app.puzzle_rpg.models.Map;
import org.keyroy.app.puzzle_rpg.models.Puzzle;
import org.keyroy.app.puzzle_rpg.models.Tile;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.graphics.Point;
import android.graphics.Rect;
import android.view.MotionEvent;
import android.view.View;

public class PuzzleView extends View {
	private Paint fillPaint;
	private Paint drawPaint;
	private Thread thread;

	private Map map;

	private Point p1 = new Point(-1, -1);
	private Point p2 = new Point(-1, -1);

	private List<List<Point>> points;
	private List<List<Point>> tips;

	public PuzzleView(Context context, Map map) {
		super(context);
		this.map = map;
		fillPaint = new Paint();
		fillPaint.setStyle(Style.FILL);
		drawPaint = new Paint();
		drawPaint.setStyle(Style.STROKE);

		fillPaint.setColor(0xfff4a460);
		drawPaint.setColor(0xff6b8e23);

	}

	@Override
	public boolean onTouchEvent(MotionEvent event) {
		if (event.getAction() == MotionEvent.ACTION_DOWN || event.getAction() == MotionEvent.ACTION_MOVE) {
			int x = (int) event.getX() / map.gridWidth;
			int y = (int) event.getY() / map.gridHeight;

			if (Puzzle.inMap(map, x, y) && Puzzle.inMap(map, p1.x, p1.y)) {
				p2.x = x;
				p2.y = y;
				int ox = Math.abs(p1.x - p2.x);
				int oy = Math.abs(p1.y - p2.y);
				boolean isLegal = ox <= 1 && oy <= 1 && (ox == 0 || oy == 0) && (ox == 1 || oy == 1);
				if (isLegal) {
					change(p1, p2);
					resetState();
				} else {
					p1.x = x;
					p1.y = y;
				}
			} else {
				p1.x = x;
				p1.y = y;
			}
		} else if (event.getAction() == MotionEvent.ACTION_UP) {
			resetState();
		}

		repaint();
		return true;
	}

	@Override
	protected void onDraw(Canvas canvas) {
		drawPoints(canvas);
		drawMap(canvas);
		drawTips(canvas);
	}

	private final void drawMap(Canvas canvas) {
		if (map != null) {
			Context context = getContext();
			for (int y = 0; y < map.rows; y++) {
				for (int x = 0; x < map.columns; x++) {
					Tile tile = map.tiles[x][y];
					if (tile != null) {
						canvas.drawBitmap(tile.getBitmap(context), tile.cx, tile.cy, null);
					}
				}
			}
		}
	}

	private final void drawPoints(Canvas canvas) {
		if (points != null) {
			Rect rect = new Rect(0, 0, map.gridWidth, map.gridHeight);
			for (int i = 0; i < points.size(); i++) {
				List<Point> list = points.get(i);
				for (int j = 0; j < list.size(); j++) {
					Point point = list.get(j);
					int px = point.x * map.gridWidth;
					int py = point.y * map.gridHeight;
					rect.set(px, py, px + map.gridWidth, py + map.gridHeight);
					if (map.tiles[point.x][point.y] != null) {
						canvas.drawRect(rect, fillPaint);
					}
				}
			}
		}

	}

	private final void drawTips(Canvas canvas) {
		if (tips != null) {
			Rect rect = new Rect(0, 0, map.gridWidth, map.getHeight());
			for (int i = 0; i < tips.size(); i++) {
				List<Point> list = tips.get(i);
				for (int j = 0; j < list.size(); j++) {
					Point point = list.get(j);
					int px = point.x * map.gridWidth;
					int py = point.y * map.gridHeight;
					rect.set(px, py, px + map.gridWidth, py + map.gridHeight);
					if (map.tiles[point.x][point.y] != null) {
						canvas.drawRect(rect, drawPaint);
					}
				}
			}
		}

	}

	private final void change(Point p1, Point p2) {
		Puzzle.change(map, p1, p2);
		repaint();
		resetState();
		points = new ArrayList<List<Point>>();
		points.addAll(Puzzle.find(map, p1));
		points.addAll(Puzzle.find(map, p2));
		auto();
	}

	private final void resetState() {
		p1.x = -1;
		p1.y = -1;
		p2.x = -1;
		p2.y = -1;
	}

	public boolean check() {
		List<List<Point>> lists = Puzzle.tips(map);
		return lists.size() > 0;
	}

	public final void auto() {
		if (thread == null || thread.isAlive() == false) {
			thread = new Thread(new Runnable() {
				boolean isFind;
				long delay = 300;

				@Override
				public void run() {
					if (points != null) {
						try {
							repaint();
							Thread.sleep(delay);
							clean();
							Thread.sleep(delay);
							drop();
							fix();
							Puzzle.flushLocation(map);
							Thread.sleep(delay);
							while (find()) {
								Thread.sleep(delay);
								clean();
								Thread.sleep(delay);
								drop();
								fix();
								Puzzle.flushLocation(map);
								Thread.sleep(delay);
							}

							if (check() == false) {
								reflush();
							}
						} catch (Exception e) {
						}
					} else if (isFind == false) {
						find();
						isFind = true;
						run();
					}
				}
			});
			thread.start();
		}

	}

	public boolean find() {
		points = Puzzle.find(map);
		repaint();
		return points.size() > 0;
	}

	public void find(Point point) {
		if (point != null && point.x >= 0 && point.y >= 0 && point.x < map.columns && point.y < map.rows) {
			points = Puzzle.find(map, point);
			repaint();
		}
	}

	public void tips() {
		tips = Puzzle.tips(map);
		repaint();
	}

	public void clean() {
		Puzzle.clean(map, points);
		reset();
		repaint();
	}

	public void drop() {
		Puzzle.dropDown(map);
		reset();
		repaint();
	}

	public void fix() {
		Puzzle.fix(map);
		reset();
		repaint();
	}

	public void reflush() {
		Puzzle.fill(map);
		reset();
		repaint();
	}

	private final void reset() {
		points = tips = null;
		repaint();
	}

	private final void repaint() {
		postInvalidate();
	}

}
